package lumis.service.portalmanagement.file;

import java.security.InvalidParameterException;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import lumis.portal.PortalContext;
import lumis.portal.PortalException;
import lumis.portal.PortalStringResource;
import lumis.portal.UnexpectedException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.dao.DaoException;
import lumis.portal.file.FileConfig;
import lumis.portal.file.FileDaoJdbc;
import lumis.portal.file.FileManager;
import lumis.portal.manager.ManagerException;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.manager.ManagerFactoryInternal;
import lumis.portal.progress.ProgressConfig;
import lumis.portal.progress.ProgressConfig.Type;
import lumis.portal.transaction.ITransaction;
import lumis.portal.transaction.PortalTransactionFactory;
import lumis.util.IResource;
import lumis.util.log.ILogger;
import lumis.util.log.LoggerFactory;

/**
 * Updates metadata from files uploaded by users.
 * 
 * @version $Revision: 13092 $ $Date: 2011-05-28 18:19:06 -0300 (Sat, 28 May 2011) $
 * @since 6.0.0
 */
public class UpdateMetadataRunner implements Runnable
{
	private static int pageSize = 25;
	private List<String> fileIds;
	private static ILogger logger = LoggerFactory.getLogger(UpdateMetadataProcessActionHandler.class);
	final private String userId;
	final private Locale userLocale;
	final private String processId;
	final private long startTime;
	private int overallProgressMax = 1;
	private int overallProgressValue = 0;
	private long totalFile;
	private long indexFile;
	private IResource resource;
	private SessionConfig sessionConfig;

	/**
	 * Updates metadata from files.
	 * 
	 * @param userId the user identifier that is going to execute the action.
	 * @param userLocale the user locale.
	 * @param processId a processId used between back-end and front-end to send and
	 *            		receive information.
	 * @since 6.0.0
	 */
	public UpdateMetadataRunner(String userId, Locale userLocale, String processId)
	{
		this.userLocale = userLocale;
		this.userId = userId;
		this.processId = processId;
		this.startTime = new Date().getTime();
		this.resource = new PortalStringResource();
		this.resource.addResourcePath("lumis/service/portalmanagement/file/strings/strings");
	}

	/**
	 * @since 6.0.0
	 */
	private ITransaction getNewTransaction() throws DaoException
	{
		ITransaction transaction = PortalTransactionFactory.createTransaction();
		transaction.begin();
		return transaction;
	}
	/**
	 * @since 6.0.0
	 */
	private void updateMetadata() throws PortalException
	{
		sendProgressNotification("STR_INITIALIZING", Type.BEGIN, null);
		FileDaoJdbc fileDaoJdbc = new FileDaoJdbc();
		logger.debug("Getting fileIds that will be updated...");
		fileIds = (List<String>) fileDaoJdbc.getFileIds(getNewTransaction());

		if (fileIds != null)
		{
			// Getting a first transaction. The number of transaction depends of
			// the pageSize
			ITransaction transaction = getNewTransaction();
			try
			{

				// Getting total files
				overallProgressMax = fileIds.size();
				totalFile = fileIds.size();
				logger.debug("Starting the update of Metadata from " + totalFile + " files...");
				
				String updateInfoMessage = 	"[lumis:localize]STR_STARTED_METADATA_UPDATE[/lumis:localize]<BR>" + 
											"[lumis:localize]STR_FILE_COUNT[/lumis:localize]: " + Long.toString(totalFile) + "<BR>";
				sendProgressNotification(updateInfoMessage, Type.INFO, null);

				FileManager fileManager = new FileManager();

				for (String fileId : fileIds)
				{
					if (indexFile % pageSize == 0)
					{
						transaction.commit();
						transaction.dispose();
						logger.debug("Updating file " + indexFile + " of " + totalFile + "...");
						transaction = getNewTransaction();
					}

					updateProgressBar(1, "STR_FILES_UPDATED;" + indexFile);
					try
					{
						fileManager.updateMetadata(fileId, transaction);
					}
					catch(Exception e)
					{
						FileConfig fileConfig = fileManager.get(sessionConfig, fileId, transaction);
						sendProgressNotification("STR_ERROR_PROCESS_FILE;"+fileConfig.getName(), Type.END_ERROR, null);
						logger.error("An error occurred while processing the file '"+fileConfig.getName()+"'", e);
					}
					indexFile++;
				}

				transaction.commit();
				logger.debug("Completed the update of Metadata from " + totalFile + " files.");
				// update finish successfully
				sendProgressFinalNotification("STR_UPDATE_FINISHED_SUCCESSFULLY", Type.END_OK);
			}
			catch (PortalException e)
			{
				sendProgressFinalNotification(e.getLocalizedMessage(), Type.END_ERROR);
				throw e;
			}
			catch (Exception e)
			{
				logger.debug("Error occurs during update process", e);
				throw new UnexpectedException(e);
			}
			finally
			{
				transaction.dispose();
			}
		}
	}

	/**
	 * @since 6.0.0
	 */
	public void run()
	{
		try
		{
			sessionConfig = ManagerFactory.getAuthenticationManager().impersonate(userId);
			try
			{
				updateMetadata();
			}
			finally
			{
				ManagerFactory.getAuthenticationManager().endImpersonation(sessionConfig);
			}
		}
		catch (Exception e)
		{
			logger.error("Error during metadata of file update", e);
		}
	}

	/**
	 * @since 6.0.0
	 */
	private void updateProgress(ProgressConfig progressConfig) throws PortalException
	{
		ITransaction transaction = PortalTransactionFactory.createTransaction();
		try
		{
			transaction.begin();

			if (!progressConfig.isLastMessage() && ManagerFactoryInternal.getProgressManager().getAbortedByProcessId(progressConfig.getProcessId()))
			{
				throw new ManagerException("STR_OPERATION_CANCELED_BY_USER");
			}

			String message = progressConfig.getMessage();
			if (message != null)
				message = ManagerFactory.getLocalizationManager().localize(message, resource, userLocale);
			progressConfig.setMessage(message);
			progressConfig.setElapsedTime((int) (new Date().getTime() - this.startTime));
			ManagerFactoryInternal.getProgressManager().addOrUpdate(progressConfig);

			transaction.commit();
		}
		finally
		{
			transaction.dispose();
		}
	}

	/**
	 * @since 6.0.0
	 */
	private void updateProgressBar(int incrementOverallProgress, String itemName) throws PortalException
	{
		this.overallProgressValue += incrementOverallProgress;
		updateProgress(new ProgressConfig(processId, overallProgressMax, overallProgressValue, 0, 0, itemName, Type.STEP, false));
	}

	/**
	 * @since 6.0.0
	 */
	private void sendProgressNotification(String message, Type type, String innerProgressId) throws PortalException
	{
		if (type.equals(Type.STEP))
			throw new InvalidParameterException("Type.STEP is invalid. Use another type.");
		ProgressConfig progressConfig = new ProgressConfig(processId, overallProgressMax, overallProgressValue, 0, 0, message, type, false);
		progressConfig.setInnerProgressId(innerProgressId);
		updateProgress(progressConfig);
	}

	/**
	 * @since 6.0.0
	 */
	private void sendProgressFinalNotification(String message, Type type) throws PortalException
	{
		updateProgress(new ProgressConfig(processId, 0, 0, 0, 0, message, type, true));
	}

}
